<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>模板</title>
  </head>
  <body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入react-dom，用于支持react操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel，用于将jsx转换成js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <script type="text/babel">
      /**
       * 组件挂载时候的函数调用流程
       * 1.constructor：构造函数
       * 2.componentWillMount：组件将要挂载前
       * 3.render：挂载组件
       * 4.componentDidMount：组件挂载完成后
       * 5.componentWillUnmount：组件将要卸载之前
       *
       * 组件修改时候的流程，有三种情况
       * 情况1：通过setState()修改状态
       * 1.shouldComponentUpdate："阀门"方法，返回一个boolean值，返回true则继续执行下去，返回false不会继续执行下去
       * 2.componentWillUpdate：组件将要更新前执行
       * 3.render:更新组件
       * 4.componentDidUpdate：组件更新后执行
       * 5.componentWillUnmount：组件将要卸载之前
       *
       * 情况2：通过forceUpdate()，不修改任何状态中的数据，强制更新一下
       * 和setState()流程的区别是：不需要经过"阀门"方法的控制，直接更新
       * 1.componentWillUpdate：组件将要更新前执行
       * 2.render:更新组件
       * 3.componentDidUpdate：组件更新后执行
       * 4.componentWillUnmount：组件将要卸载之前
       *
       * 情况3：父组件render,子组件会触发一下流程
       * 1.componentWillReceiveProps:组件将要接收属性(父组件)
       * 2.shouldComponentUpdate:组件是否应该更新，返回boolean值，true更新，false不更新
       * 3.componentWillUpdate:组件更新前（将要更新）的钩子
       * 4.componentDidUpdate:组件更新后的钩子
       */

      /**
       * 1.初始化阶段：有ReactDOM。render()触发---初次渲染
       *   1)constructor()
       *   2)componentWillMount()
       *   3)render()
       *   4)componentDidMount() --> 常用
       *       这个钩子主要做一些初始化的事，如：开启定时器，发送网络请求，订阅消息
       * 2.更新阶段：组件内容this。setState或父组件render触发
       *   1)shouldComponentUpdate()
       *   2)componentWillUpdate()
       *   3)render()
       *   4)componentDidUpdate()
       * 3.卸载组件：有ReactDOM.unmountComponentAtNode()触发
       *   1.componentWillUnmount() --> 常用
       *       一般做收尾工作：关闭定时器，取消消息订阅
       */
      class Count extends React.Component {
        //-----------------------创建时--------------------------
        //构造器
        constructor(props) {
          console.log("Count-constructor");
          super(props);
          //初始化状态
          this.state = {
            count: 0,
          };
        }

        //组件将要挂载的钩子
        componentWillMount() {
          console.log("componentWillMount...");
        }

        //加1按钮回调
        add = () => {
          //获取原状态
          const { count } = this.state;
          //更新状态
          this.setState({
            count: count + 1,
          });
        };

        //卸载组件按钮的回调
        death = () => {
          ReactDOM.unmountComponentAtNode(document.querySelector("#test"));
        };

        //强制更新按钮的回调
        force = () => {
          this.forceUpdate();
        };

        //挂载组件
        render() {
          console.log("Count-render");
          return (
            <div>
              <h2>当前求和为：{this.state.count}</h2>
              <button onClick={this.add}>点击+1</button>
              <button onClick={this.death}>卸载组件</button>
              <button onClick={this.force}>强制更新</button>
            </div>
          );
        }

        //组件挂载完成后的狗子
        componentDidMount() {
          console.log("Count-componentDidMount");
        }

        //组件将要卸载之前的钩子
        componentWillUnmount() {
          console.log("Count-componentWillUnmount");
        }

        //---------------------------修改时--------------------------------
        /**
         * 控制组件更新的'阀门'
         * 当更新状态的时候，首先会调用该方法，该方法返回一个boolean值
         * 返回true，则可以继续执行下去修改值
         * 返回false，则修改失败,后续方法不再执行
         */
        shouldComponentUpdate() {
          console.log("Count-shouldComponentUpdate");
          return true;
        }

        //组件将要更新时的钩子
        componentWillUpdate() {
          console.log("Count-componentWillUpdate");
        }

        //组件更新后的钩子
        componentDidUpdate() {
          console.log("Count-componentDidUpdate");
        }
      }

      //父组件
      class A extends React.Component {
        state = {
          carName: "奥迪",
        };

        changeCar = () => {
          this.setState({
            carName: "布加迪威龙",
          });
        };

        /**
         * 在这里A是B的父组件，可以通过浏览器的react插件查看层级关系
         * 当父组件一旦重新render，子组件就会响应componentWillReceiveProps这个生命周期的路线
         * componentWillReceiveProps：标识组件将要接受属性properties
         */
        render() {
          return (
            <div>
              <div>我是A组件</div>
              <button onClick={this.changeCar}>换车</button>
              <B carName={this.state.carName}></B>
            </div>
          );
        }
      }

      //子组件
      class B extends React.Component {
        //这方法有个坑，第一次渲染时，不会调用该钩子，之后再之后更新才会调用这个钩子
        componentWillReceiveProps(props) {
          console.log("B-componentWillReceiveProps-", props);
        }

        shouldComponentUpdate() {
          console.log("Count-shouldComponentUpdate");
          return true;
        }

        //组件将要更新时的钩子
        componentWillUpdate() {
          console.log("Count-componentWillUpdate");
        }

        //组件更新后的钩子
        componentDidUpdate() {
          console.log("Count-componentDidUpdate");
        }

        render() {
          return (
            <div>
              <div>我是B组件，我有一辆车：{this.props.carName}</div>
            </div>
          );
        }
      }

      ReactDOM.render(<A />, document.querySelector("#test"));
    </script>
  </body>
</html>
